﻿// <copyright file="VoiceTrigger.cs" company="Ucaya">
// </copyright>
// <author>Ucaya</author>

namespace KinectExtensions.Interactivity
{
    #region

    using System;
    using System.ComponentModel;
    using System.ComponentModel.Composition;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    using System.Windows.Media;

    using KinectExtensions.Helpers;
    using KinectExtensions.Nui;
    using KinectExtensions.Services;

    using DesignerProperties = KinectExtensions.DesignerProperties;

    #endregion

    /// <summary>
    ///   The voice trigger.
    /// </summary>
    public class VoiceTrigger : TriggerBase<FrameworkElement>
    {
        #region Constants and Fields

        /// <summary>
        ///   BarHintText depedency property.
        /// </summary>
        public static readonly DependencyProperty BarHintTextProperty = DependencyProperty.Register("BarHintText", typeof(string), typeof(VoiceTrigger), new PropertyMetadata((d, e) => ((VoiceTrigger)d).OnBarHintTextChanged((string)e.OldValue, (string)e.NewValue)));

        /// <summary>
        ///   The confidence property.
        /// </summary>
        public static readonly DependencyProperty ConfidenceProperty = DependencyProperty.Register("Confidence", typeof(float), typeof(VoiceTrigger), new PropertyMetadata(0.9f));

        /// <summary>
        ///   HintTemplate depedency property.
        /// </summary>
        public static readonly DependencyProperty HintTemplateProperty = DependencyProperty.Register("HintTemplate", typeof(DataTemplate), typeof(VoiceTrigger), new PropertyMetadata((d, e) => ((VoiceTrigger)d).OnHintTemplateChanged((DataTemplate)e.OldValue, (DataTemplate)e.NewValue)));

        /// <summary>
        ///   VoiceString depedency property.
        /// </summary>
        public static readonly DependencyProperty VoiceStringProperty = DependencyProperty.Register("VoiceString", typeof(string), typeof(VoiceTrigger), new PropertyMetadata((d, e) => ((VoiceTrigger)d).OnVoiceStringChanged((string)e.OldValue, (string)e.NewValue)));

        private ContentControl contentControl;

        private VoiceCommand registeredVoiceCommand;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///   Initializes a new instance of the <see cref = "VoiceTrigger" /> class.
        /// </summary>
        public VoiceTrigger()
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            ServiceLocator.Instance.SatisfyImportsOnce(this);

            this.InteractionManager.CurrentInteractionModeChanged += this.InteractionManager_CurrentInteractionModeChanged;
        }

        #endregion

        #region Public Properties

        public string BarHintText
        {
            get
            {
                return (string)this.GetValue(BarHintTextProperty);
            }

            set
            {
                this.SetValue(BarHintTextProperty, value);
            }
        }

        /// <summary>
        ///   Gets or sets Confidence.
        /// </summary>
        public float Confidence
        {
            get
            {
                return (float)this.GetValue(ConfidenceProperty);
            }

            set
            {
                this.SetValue(ConfidenceProperty, value);
            }
        }

        public DataTemplate HintTemplate
        {
            get
            {
                return (DataTemplate)this.GetValue(HintTemplateProperty);
            }

            set
            {
                this.SetValue(HintTemplateProperty, value);
            }
        }

        [Import]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public InteractionManager InteractionManager { get; set; }

        [Import]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public OverlayService OverlayService { get; private set; }

        /// <summary>
        ///   Gets or sets VoiceService.
        /// </summary>
        [Import]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public VoiceService VoiceService { get; set; }

        /// <summary>
        ///   Gets or sets VoiceString.
        /// </summary>
        public string VoiceString
        {
            get
            {
                return (string)this.GetValue(VoiceStringProperty);
            }

            set
            {
                this.SetValue(VoiceStringProperty, value);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        ///   The on attached.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.Loaded += this.AssociatedObject_Loaded;
            this.AssociatedObject.Unloaded += this.AssociatedObject_Unloaded;

            if (this.AssociatedObject.IsLoaded)
            {
                this.ClearVoiceCommand();
            }
            this.SetVoiceCommand();
        }

        /// <summary>
        ///   The on detaching.
        /// </summary>
        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.Loaded -= this.AssociatedObject_Loaded;
            this.AssociatedObject.Unloaded -= this.AssociatedObject_Unloaded;
            this.ClearVoiceCommand();
        }

        private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
        {
            this.SetVoiceCommand();
        }

        private void AssociatedObject_Unloaded(object sender, RoutedEventArgs e)
        {
            this.ClearVoiceCommand();
        }

        private void ClearVoiceCommand()
        {
            if (this.registeredVoiceCommand != null)
            {
                this.VoiceService.VoiceCommands.Remove(this.registeredVoiceCommand);
                this.registeredVoiceCommand = null;
            }
        }

        private void InteractionManager_CurrentInteractionModeChanged(object sender, EventArgs e)
        {
            this.RefreshVoiceAdorner();
        }

        /// <summary>
        ///   handles the BarHintTextProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnBarHintTextChanged(string oldValue, string newValue)
        {
        }

        /// <summary>
        ///   handles the HintTemplateProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnHintTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
        {
        }

        /// <summary>
        ///   handles the VoiceStringProperty changes.
        /// </summary>
        /// <param name = "oldValue">
        ///   The old value.
        /// </param>
        /// <param name = "newValue">
        ///   The new value.
        /// </param>
        private void OnVoiceStringChanged(string oldValue, string newValue)
        {
            if (this.AssociatedObject == null)
            {
                return;
            }

            this.ClearVoiceCommand();
            this.SetVoiceCommand();
        }

        private void RefreshVoiceAdorner()
        {
            if (this.InteractionManager.CurrentInteractionMode == Interactions.Voice && this.contentControl == null)
            {
                Panel layer = null;
                if (this.OverlayService == null || this.OverlayService.OverlayProvider == null || (layer = this.OverlayService.OverlayProvider.GetOverlayLayer()) == null || this.HintTemplate == null)
                {
                    return;
                }

                this.contentControl = new ContentControl { Content = this, ContentTemplate = this.HintTemplate };
                GeneralTransform transform = this.AssociatedObject.TransformToVisual(layer);
                Rect bounds = CustomVisualTreeHelper.GetHitableDescendantBounds(this.AssociatedObject);
                bounds = transform.TransformBounds(bounds);
                this.contentControl.Width = bounds.Width;
                this.contentControl.Height = bounds.Height;
                this.contentControl.RenderTransform = new TranslateTransform { X = bounds.X, Y = bounds.Y };
                layer.Children.Add(this.contentControl);
            }
            else if (this.InteractionManager.CurrentInteractionMode != Interactions.Voice && this.contentControl != null)
            {
                if (this.contentControl.Parent != null)
                {
                    ((Panel)this.contentControl.Parent).Children.Remove(this.contentControl);
                }

                this.contentControl = null;
            }
        }

        private void SetVoiceCommand()
        {
            if (!string.IsNullOrEmpty(this.VoiceString))
            {
                this.registeredVoiceCommand = new VoiceCommand((prm, confidence) => this.WordRecognized(confidence), this.VoiceString);
                this.VoiceService.VoiceCommands.Add(this.registeredVoiceCommand);
            }
        }

        /// <summary>
        ///   The launch command.
        /// </summary>
        /// <param name = "confidence">
        ///   The confidence.
        /// </param>
        private void WordRecognized(float confidence)
        {
            if (confidence >= this.Confidence)
            {
                this.InvokeActions(null);
            }
        }

        #endregion
    }
}